package frame

import (
	"encoding/binary"
	"fmt"
	"io"
)

// 数据帧frame应该也是需要接口约束，可以完成数据帧的收发
/* 建立了这个抽象之后，表示frame这个结构主要的动作 */
type MyFramePayload []byte

type StreamFrameCodec interface {
	// 解码表示从IO接口中拿取出来
	Decode(io.Reader) (MyFramePayload, error)
	// 解码表示将frame发送到IO接口中
	Encode(io.Writer, MyFramePayload) error
}

// 在定义了MyFramePayload类型，定义了作为一个数据帧frame应该有的接口方法，
// 接下来应该完成自己的frame定义

type MyFrame struct{} // 自定义的frame类型

// 为自己的frame实现方法

func NewMyFrame() StreamFrameCodec {
	return &MyFrame{}
}

func (f *MyFrame) Encode(w io.Writer, framePayload MyFramePayload) error {
	var totalLen int32 = int32(len(framePayload)) + 4

	// 这里类似于
	// binary这个包类似于json，都是做编码工作的。这里是需要将int转码为[]byte
	// 这里的写入totalLen是关键，主要将totalLen变成binary的形式写入到W中
	err := binary.Write(w, binary.BigEndian, &totalLen)
	if nil != err {
		return err
	}
	// 长度写入了，然后就写入数据
	var farmeCopy = framePayload //
	n, err := w.Write(farmeCopy)
	if nil != err {
		return fmt.Errorf("write to stream error:%v", err)
	}
	if n != len(framePayload) {
		return fmt.Errorf("too short")
	}
	// 其实有一个问题，这边其实会产生并发写的问题吧？经过尝试，写Writer其实防不住并发写的
	// 那么问题就是，在实际运行的过程中，会不会产生并发写的问题？
	return nil
}

func (f *MyFrame) Decode(r io.Reader) (MyFramePayload, error) {
	// 先读取长度，然后再一次性读取出来转化并返回
	// r.Read()
	var totalLen int32
	err := binary.Read(r, binary.BigEndian, &totalLen) // 先读取int32(4 byte)得到长度
	if nil != err {
		return nil, err
	}

	// buf := make([]byte, 0, totalLen-4)  // 居然这么奇怪？io流读取的时候和slice的长度有关
	buf := make([]byte, totalLen-4)
	n, err := io.ReadFull(r, buf)
	if nil != err {
		return nil, err
	}
	if n != int(totalLen)-4 {
		return nil, fmt.Errorf("short read")
	}
	return MyFramePayload(buf), nil

}
